package com.jack006.article.service.impl;

import com.github.pagehelper.PageHelper;
import com.jack006.api.config.RabbitMQConfig;
import com.jack006.api.config.RabbitMQDelayConfig;
import com.jack006.api.service.BaseService;
import com.jack006.article.mapper.ArticleMapper;
import com.jack006.article.mapper.ArticleMapperCustom;
import com.jack006.article.service.ArticleService;
import com.jack006.enums.ArticleAppointType;
import com.jack006.enums.ArticleReviewLevel;
import com.jack006.enums.ArticleReviewStatus;
import com.jack006.enums.YesOrNo;
import com.jack006.exception.GraceException;
import com.jack006.grace.result.ResponseStatusEnum;
import com.jack006.pojo.Article;
import com.jack006.pojo.Category;
import com.jack006.pojo.bo.NewArticleBO;
import com.jack006.pojo.eo.ArticleEO;
import com.jack006.utils.PagedGridResult;
import com.mongodb.client.gridfs.GridFSBucket;
import org.apache.commons.lang3.StringUtils;
import org.bson.types.ObjectId;
import org.n3r.idworker.Sid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.IndexQueryBuilder;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;
import tk.mybatis.mapper.entity.Example;

import java.util.Date;
import java.util.List;

/**
 * @Classname ArticleServiceImpl
 * @Description 文章
 * @Date 2021-07-09 21:13
 * @Created by jack
 */
@Service("articleService")
public class ArticleServiceImpl extends BaseService implements ArticleService {

    private static final Logger logger = LoggerFactory.getLogger(ArticleServiceImpl.class);

    @Autowired
    private ArticleMapper articleMapper;
    @Autowired
    private ArticleMapperCustom articleMapperCustom;
    @Autowired
    private Sid sid;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private GridFSBucket gridFSBucket;
    @Autowired
    private RabbitTemplate rabbitTemplate;
//    @Autowired
//    private AliTextReviewUtils aliTextReviewUtils;
    @Autowired
    private ElasticsearchTemplate esTemplate;

    @Transactional
    @Override
    public void createCategory(NewArticleBO newArticleBO, Category category) {

        String articleId = sid.nextShort();

        Article article = new Article();
        BeanUtils.copyProperties(newArticleBO,article);
        article.setId(articleId);

        article.setCategoryId(category.getId());
        article.setArticleStatus(ArticleReviewStatus.REVIEWING.type);
        article.setCommentCounts(0);
        article.setReadCounts(0);

        article.setIsDelete(YesOrNo.NO.type);
        article.setCreateTime(new Date());
        article.setUpdateTime(new Date());

        if (newArticleBO.getIsAppoint() == ArticleAppointType.TIMING.type) {
            article.setPublishTime(newArticleBO.getPublishTime());
        } else {
            article.setPublishTime(new Date());
        }

        int result = articleMapper.insert(article);
        if (result != 1) {
            GraceException.display(ResponseStatusEnum.ARTICLE_CREATE_ERROR);
        }

        // 发送延时消息到mq,计算定时发布时间和当前时间的时间差，为往后的延时时间
        if(article.getIsAppoint() == ArticleAppointType.TIMING.type) {

            Date endDate = article.getPublishTime();
            Date startDate = new Date();

            int delayTimes = (int)(endDate.getTime() - startDate.getTime());

            MessagePostProcessor messagePostProcessor = new MessagePostProcessor() {
                @Override
                public Message postProcessMessage(Message message) throws AmqpException {
                    // 设置消息的持久
                    message.getMessageProperties()
                            .setDeliveryMode(MessageDeliveryMode.PERSISTENT);
                    // 设置消息的延迟的时间
                    message.getMessageProperties()
                            .setDelay(delayTimes);

                    return message;
                }
            };

            rabbitTemplate.convertAndSend(
                    RabbitMQDelayConfig.EXCHANGE_DELAY,
                    "publish.delay.display",
                    articleId,
                    messagePostProcessor
            );

            logger.info("发送延时发布的文章"+new Date());
        }

        // 通过阿里 AI 自动审核文章
        // String reviewTextContent = aliTextReviewUtils.reviewTextContent(newArticleBO.getContent());
        String reviewTextContent = ArticleReviewStatus.WAITING_MANUAL.type.toString();
        if (reviewTextContent.equals(ArticleReviewLevel.PASS.type)) {
            // 修改当前文章,状态标记为审核通过
            this.updateArticleReviewStatus(articleId,ArticleReviewStatus.SUCCESS.type);
        } else if ( reviewTextContent.equals(ArticleReviewLevel.REVIEW.type)){
            // 修改当前的文章，状态标记为需要人工骨审核
            this.updateArticleReviewStatus(articleId,ArticleReviewStatus.WAITING_MANUAL.type);
        } else if (reviewTextContent.equals(ArticleReviewLevel.BLOCK.type)) {
            // 修改当前的文章，状态标记为审核未通过
            this.updateArticleReviewStatus(articleId,ArticleReviewStatus.FAILED.type);
        }
    }

    @Transactional
    @Override
    public void updateAppointToPublish() {
        articleMapperCustom.updateAppointToPublish();
    }


    @Transactional
    @Override
    public void updateArticleToPublish(String articleId) {
        articleMapperCustom.updateArticleToPublish(articleId);
    }

    @Override
    public PagedGridResult queryArticleList(String userId, String keyword, Integer status,
                                            Date startDate, Date endDate,
                                            Integer page, Integer pageSize) {


        Example example = new Example(Article.class);
        Example.Criteria criteria = example.createCriteria();

        example.orderBy("createTime").desc();

        if (StringUtils.isNotBlank(keyword)) {
            criteria.andLike("title","%"+keyword+"%");
        }

        if (ArticleReviewStatus.isArticleStatusValid(status)) {
            criteria.andEqualTo("articleStatus",status);
        }

        if (status != null && status == 12) {
            criteria.andEqualTo("articleStatus",ArticleReviewStatus.REVIEWING.type)
                    .orEqualTo("articleStatus",ArticleReviewStatus.WAITING_MANUAL.type);
        }
        criteria.andEqualTo("isDelete",YesOrNo.NO.type);
        if (startDate != null) {
            criteria.andGreaterThanOrEqualTo("publishTime",startDate);
        }

        if (endDate != null) {
            criteria.andLessThanOrEqualTo("publishTime",endDate);
        }

        PageHelper.startPage(page,pageSize);
        List<Article> articles = articleMapper.selectByExample(example);
        PagedGridResult result = setterPagedGrid(articles, page);
        return result;
    }

    @Override
    public void updateArticleReviewStatus(String articleId, Integer pendingStatus) {
        Example example = new Example(Article.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("id",articleId);
        Article  updateArticle = new Article();
        updateArticle.setArticleStatus(pendingStatus);

        int res = articleMapper.updateByExampleSelective(updateArticle, example);
        if (res != 1) {
            GraceException.display(ResponseStatusEnum.ARTICLE_REVIEW_ERROR);
        }

        // 如果审核通过，则查询 article,把相应数据的字段存入 es中
        if (pendingStatus == ArticleReviewStatus.SUCCESS.type) {
            Article result = articleMapper.selectByPrimaryKey(articleId);
            // 如果是即时发布的文章，审核通过后则可以直接存入 es 中
            if (result.getIsAppoint() == ArticleAppointType.IMMEDIATELY.type) {
                ArticleEO articleEO = new ArticleEO();
                BeanUtils.copyProperties(result,articleEO);
                IndexQuery indexQuery = new IndexQueryBuilder().withObject(articleEO).build();
                esTemplate.index(indexQuery);
            }
            // 如果是定时发布，此处不会存入 es中，需要在定时的延时队列中执行
        }
    }

    @Override
    public PagedGridResult queryArticleListAdmin(Integer status, Integer page, Integer pageSize) {
        Example example = new Example(Article.class);
        example.orderBy("createTime").desc();

        Example.Criteria criteria = example.createCriteria();
        if (ArticleReviewStatus.isArticleStatusValid(status)) {
            criteria.andEqualTo("articleStatus",status);

        }

        // 审核中是机审和人审核的两个状态，需要单独判断
        if (status != null && status == 12) {
            criteria.andEqualTo("articleStatus",ArticleReviewStatus.REVIEWING.type)
                    .orEqualTo("articleStatus",ArticleReviewStatus.WAITING_MANUAL.type);
        }

        criteria.andEqualTo("isDelete",YesOrNo.NO.type);

        PageHelper.startPage(page,pageSize);
        List<Article> articles = articleMapper.selectByExample(example);
        return setterPagedGrid(articles, page);
    }

    @Transactional
    @Override
    public void deleteArticle(String userId, String articleId) {
        Example example = makeExampleCriteria(userId,articleId);
        Article article = new Article();
        article.setIsDelete(YesOrNo.YES.type);

        int result = articleMapper.updateByExampleSelective(article, example);
        if (result != 1) {
            GraceException.display(ResponseStatusEnum.ARTICLE_DELETE_ERROR);
        }
        deleteHtml(articleId);
        // deleteHtmlByMQ(articleId);

        esTemplate.delete(ArticleEO.class,articleId);
    }

    public void deleteHtmlByMQ(String articleId){
        rabbitTemplate.convertAndSend(RabbitMQConfig.EXCHANGE_ARTICLE,
                "article.delete.do",
                articleId);
    }

    /**
     * 文章撤回删除后，删除静态化的 html
     * @param articleId
     */
    private void deleteHtml(String articleId) {
        // 1. 查询文章的mongoFileId
        Article pending = articleMapper.selectByPrimaryKey(articleId);
        String articleMongoId = pending.getMongoFileId();

        if (StringUtils.isNotBlank(articleMongoId)) {
            return;
        }
        // 2. 删除GridFS上的文件
        gridFSBucket.delete(new ObjectId(articleMongoId));

        // 3. 删除消费端的HTML文件
        doDeleteArticleHTML(articleId);

    }

    private void doDeleteArticleHTML(String articleId) {

        String serviceId = "SERVICE-ARTICLE-HTML";
        String url = "http://"+serviceId+"/article/html/delete?articleId=" + articleId;
        ResponseEntity<Integer> responseEntity = restTemplate.getForEntity(url, Integer.class);
        int status = responseEntity.getBody();
//        Integer status = null;
//        try {
//            status = articleHtmlControllerApi.delete(articleId);
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
        if (status != HttpStatus.OK.value()) {
            GraceException.display(ResponseStatusEnum.SYSTEM_OPERATION_ERROR);
        }
    }

    @Transactional
    @Override
    public void withdrawArticle(String userId, String articleId) {
        Example example = makeExampleCriteria(userId,articleId);
        Article article = new Article();
        article.setArticleStatus(ArticleReviewStatus.WITHDRAW.type);

        int result = articleMapper.updateByExampleSelective(article, example);
        if (result != 1) {
            GraceException.display(ResponseStatusEnum.ARTICLE_WITHDRAW_ERROR);
        }

        esTemplate.delete(ArticleEO.class,articleId);

    }

    @Transactional
    @Override
    public void updateArticleToGridFs(String articleId, String articleHtmlGridId) {
        Example articleExample = new Example(Article.class);
        Example.Criteria criteria = articleExample.createCriteria();
        criteria.andEqualTo("id", articleId);

        Article article = new Article();
        article.setMongoFileId(articleHtmlGridId);

        int result = articleMapper.updateByExampleSelective(article,articleExample);
        if (result != 1) {
            GraceException.display(ResponseStatusEnum.ARTICLE_WITHDRAW_ERROR);
        }
    }

    private Example makeExampleCriteria(String userId, String articleId) {
        Example articleExample = new Example(Article.class);
        Example.Criteria criteria = articleExample.createCriteria();
        criteria.andEqualTo("publishUserId", userId);
        criteria.andEqualTo("id", articleId);
        return articleExample;
    }
}
